package com.zzyl.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.NumberUtil;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.zzyl.base.PageResponse;
import com.zzyl.constant.CacheConstants;
import com.zzyl.dto.AlertRuleDto;
import com.zzyl.entity.AlertData;
import com.zzyl.entity.AlertRule;
import com.zzyl.mapper.AlertDataMapper;
import com.zzyl.mapper.AlertRuleMapper;
import com.zzyl.mapper.DeviceMapper;
import com.zzyl.mapper.UserRoleMapper;
import com.zzyl.service.AlertRuleService;
import com.zzyl.utils.ObjectUtil;
import com.zzyl.utils.StringUtils;
import com.zzyl.vo.AlertRuleVo;
import com.zzyl.vo.DeviceDataVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author sjqn
 */
@Slf4j
@Service
public class AlertRuleServiceImpl implements AlertRuleService {

    @Autowired
    private AlertRuleMapper alertRuleMapper;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private DeviceMapper deviceMapper;

    /**
     * 新增报警规则
     * @param alertRuleDto 报警规则请求模型
     */
    @Override
    public void createRule(AlertRuleDto alertRuleDto) {
        AlertRule alertRule = BeanUtil.toBean(alertRuleDto, AlertRule.class);
        alertRuleMapper.insert(alertRule);
    }

    /**
     * 分页条件查询
     *
     * @param pageNum       页码
     * @param pageSize      页面大小
     * @param alertRuleName 报警规则名称
     * @param productKey    产品key
     * @param functionName  功能名称
     * @return 分页结果
     */
    @Override
    public PageResponse<AlertRuleVo> getAlertRulePage(Integer pageNum, Integer pageSize, String alertRuleName, String productKey, String functionName) {
        PageHelper.startPage(pageNum, pageSize);
        Page<AlertRuleVo> page = alertRuleMapper.page(alertRuleName, productKey, functionName);
        //添加提示
        page.getResult().forEach(alertRuleVo -> {
            String alertReason = CharSequenceUtil.format("{}{}{},持续{}个周期就报警",
                                            alertRuleVo.getFunctionName(),
                                            alertRuleVo.getOperator(),
                                            alertRuleVo.getValue(),
                                            alertRuleVo.getDuration());
            alertRuleVo.setRules(alertReason);
        });

        return PageResponse.of(page, AlertRuleVo.class);
    }

    /**
     * 删除报警
     *
     * @param id 报警规则id
     */
    @Override
    public void deleteAlertRule(Long id) {
        alertRuleMapper.deleteByPrimaryKey(id);
    }

    /**
     * 修改报警
     *
     * @param id           报警规则id
     * @param alertRuleDto 报警规则更新请求模型
     */
    @Override
    public void updateAlertRule(Long id, AlertRuleDto alertRuleDto) {
        AlertRule alertRule = BeanUtil.toBean(alertRuleDto, AlertRule.class);
        alertRuleMapper.updateByPrimaryKeySelective(alertRule);
    }

    /**
     * 查询报警信息
     *
     * @param id 报警规则id
     * @return 报警信息
     */
    @Override
    public AlertRuleVo readAlertRule(Long id) {
        AlertRule alertRule = alertRuleMapper.selectByPrimaryKey(id);
        return BeanUtil.toBean(alertRule, AlertRuleVo.class);
    }

    /**
     * 启用或禁用
     *
     * @param id     报警id
     * @param status 报警规则状态
     */
    @Override
    public void enableOrDisable(Long id, Integer status) {
        alertRuleMapper.updateStatus(id, status);
    }

    @Override
    public void alertFilter(DeviceDataVo deviceDataVo) {
        //判断当前上报的时间是否超过1分钟，过滤迟来的消息
        LocalDateTime alarmTime = deviceDataVo.getAlarmTime();

        //获得alarmTime到现在时间的秒数
        long between = LocalDateTimeUtil.between(alarmTime, LocalDateTime.now(), ChronoUnit.SECONDS);
        //如果超过1分钟，则不处理
        if (between > 60) {
            return;
        }
        //查询该产品下所有的物模型下的规则
        List<AlertRule> allAlertRules1 = alertRuleMapper.selectByFunctionId(deviceDataVo.getFunctionId(), "-1", deviceDataVo.getProductKey());

//查询该产品下，该设备下，该物模型下的规则
        List<AlertRule> allAlertRules2 = alertRuleMapper.selectByFunctionId(deviceDataVo.getFunctionId(), deviceDataVo.getIotId(), deviceDataVo.getProductKey());

//合并
        Collection<AlertRule> allAlertRules = CollUtil.addAll(allAlertRules1, allAlertRules2);

        //如果没有规则，则不处理
        if (CollUtil.isEmpty(allAlertRules)) {
            return;
        }
//遍历规则，处理每一条数据
        allAlertRules.forEach(rule -> deviceDateAlarmHandler(deviceDataVo, rule));

    }

    /**
     * 规则数据处理程序
     * @param deviceDataVo 某一条数据
     * @param alertAllRule   某一条规则
     */
    private void deviceDateAlarmHandler(DeviceDataVo deviceDataVo, AlertRule alertAllRule) {

//判断生效时间

        String[] split = alertAllRule .getAlertEffectivePeriod().split("~");
        LocalTime startTime = LocalTime.parse(split[0]);
        LocalTime endTime = LocalTime.parse(split[1]);

        //获得上报时间
        LocalTime localTime = deviceDataVo.getAlarmTime().toLocalTime();
        //       05:00:00   localTime=04:00:00
        //   05:00:00                          22:59:59

        //如果在开始时间之前，直接返回，如果在结束时间之后，直接返回
        if (localTime.isBefore(startTime) || localTime.isAfter(endTime)){
            log.info("时间不在规则生效时间之内,不会报警");
            return;
        }
        String iotId = deviceDataVo.getIotId();

        //第二次校验，判断数据阙值

        //获取当前值和阈值的差值，可能是正数 也可以是负数，但是要集合 运算符来判断
        //compare(x,y)  如果x=y 返回compare=0 如果x>y 返回大于0--正数。如果x<y 返回负数
        int compare = NumberUtil.compare(Double.valueOf(deviceDataVo.getDataValue()),
                alertAllRule.getValue());

        //获取规则中的运算符 >= 或者<

        if ((alertAllRule.getOperator().equals(">=") && compare >= 0)||(alertAllRule.getOperator().equals("<") && compare < 0) ) {
            log.info("当前设备上报的数据，符合规则，准备进一步检测！！！ 阙值：{} 现值：{}", alertAllRule.getValue(), deviceDataVo.getDataValue());
        }else {
            log.info("当前设备上报的数据，不符合规则，不报警！！！ 阙值：{} 现值：{}", alertAllRule.getValue(), deviceDataVo.getDataValue());

            //todo 删除redis中保存的报警数量 (假设是3为周期，目前是2，也要删除，因为要连续）
            return;

        }
//todo 查询redis中存储的沉默周期
        //获得存在redis中的沉默周期key :silence :设备id ：功能id 规则 如;睡眠带
        String silenceCacheKey = "silence" +iotId + deviceDataVo.getFunctionId() +":"+alertAllRule.getId();

String silenceCacheData =redisTemplate.opsForValue().get(silenceCacheKey);
if (StringUtils.isNotEmpty(silenceCacheData)){
    //如果沉默数据是空的，说明可以报警，如果有值，等着沉默时间结束
    log.info("当前设备：{}  功能：{}  规则：{}  在沉默时间内，不会报警",iotId,deviceDataVo.getFunctionId(),alertAllRule.getId());
return;
}
log.info("当前设备：{}  功能：{}  规则：{}  不在沉默时间内，会报警",iotId,deviceDataVo.getFunctionId(),alertAllRule.getId());

    }


}
